package me.leiusual.sharing.util.tool

import com.google.gson.Gson
import com.google.gson.JsonParseException
import com.google.gson.TypeAdapter
import com.google.gson.TypeAdapterFactory
import com.google.gson.reflect.TypeToken
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonWriter
import kotlin.reflect.KClass
import kotlin.reflect.full.declaredMemberProperties
import kotlin.reflect.jvm.isAccessible

/**
 * author: leiusual
 * create on: 2018/11/15
 * description: 主要用于检查接口返回的字段，避免出现 kotlin notnull 但实际为 null 的问题.
 */
@Suppress("UNCHECKED_CAST")
class GsonKotlinAdapterFactory : TypeAdapterFactory {

  private val Class<*>.isKotlinClass: Boolean
    get() = declaredAnnotations.find { it.annotationClass.java.name == "kotlin.Metadata" } != null

  override fun <T : Any> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
    if (!type.rawType.isKotlinClass) {
      return null
    }
    val kClass = (type.rawType as Class<*>).kotlin
    return Adapter(this, gson, type, kClass as KClass<T>)
  }

  class Adapter<T : Any>(
      factory: TypeAdapterFactory,
      gson: Gson,
      type: TypeToken<T>,
      private val kClass: KClass<T>
  ) : TypeAdapter<T>() {

    private val delegate: TypeAdapter<T> = gson.getDelegateAdapter(factory, type)

    override fun write(out: JsonWriter?, value: T) {
      delegate.write(out, value)
    }

    override fun read(`in`: JsonReader?): T? {
      return delegate.read(`in`)?.apply { doNullCheck(this) }
    }

    private fun doNullCheck(value: T) {
      kClass.declaredMemberProperties.forEach { prop ->
        prop.isAccessible = true
        if (!prop.returnType.isMarkedNullable && prop(value) == null) {
          throw JsonParseException("解析'${prop.name}'字段为null了！")
        }
      }
    }
  }
}